package win.larryzeal.threadpool;

import java.util.LinkedList;
import java.util.List;

/**
 * 简化版。
 * 线程池的思路，就是线程固定，但执行的任务变化 - 如何做到这一点？必须是循环啊！循环从某个地方获取新的任务。
 * <p>
 * Created by LarryZeal on 2017/9/19.
 */
public class ThreadPoolManager implements ThreadPool {
    /*** 默认线程的数量*/
    private static int workerNum = 5;
    /*** 工作线程组*/
    private WorkerThread[] workerThreads;
    /***在执行的任务数量*/
    private static volatile int executeTaskNumber = 0;
    /*** 任务队列，作为一个缓冲，注意，List线程不安全*/
    private List<Runnable> taskQueue = new LinkedList<>();

    private ThreadPoolManager(){
        this(workerNum);
    }

    private ThreadPoolManager(int workerNum){
        this.workerNum = workerNum;
        workerThreads = new WorkerThread[workerNum];
        for(int i = 0; i < workerNum; i++){
            workerThreads[i] = new WorkerThread("WorkerThread-" + i);
            workerThreads[i].start();
            System.out.println("WorkerThread-" + i + " started!");
        }
    }

    public static ThreadPoolManager newFixedThreadPool(int workerNum){
        return new ThreadPoolManager(workerNum);
    }

    @Override
    public void execute(Runnable task){
        synchronized(taskQueue){
            taskQueue.add(task);
            taskQueue.notifyAll();
        }
    }

    @Override
    public void execute(Runnable[] tasks){
        synchronized(taskQueue){
            for(Runnable task : tasks){
                taskQueue.add(task);
            }
            taskQueue.notifyAll();
        }
    }

    @Override
    public void execute(List<Runnable> tasks){
        synchronized(taskQueue){
            taskQueue.addAll(tasks);
            taskQueue.notifyAll();
        }
    }

    @Override
    public int getExecuteTaskNumber(){
        return executeTaskNumber;
    }

    @Override
    synchronized public int getWaitTaskNumber(){
        return taskQueue.size();
    }

    @Override
    public int getWorkThreadNumber(){
        return workerNum;
    }

    @Override
    public void shutdown(){
        boolean flag;
        while(true){
            synchronized(taskQueue){ // TODO 千万不要在同步代码块中无限循环，无法释放锁！！！见下面的shutdown_bak() -- 导致死锁
                flag = taskQueue.isEmpty();
            }
            if(flag){
                for(int i = 0; i < workerNum; i++){
                    System.out.println("----stop threadpool-" + i);
                    workerThreads[i].stopWorker();
                }
                break;
            }
        }
    }

    /**
     * FIXME bad demo "infinite-loop within synchronized block" which is definitely forbidden!
     */
    @Deprecated
    public void shutdown_bak(){

        throw new RuntimeException("不要执行这个方法！这个方法是错误的！！！留着是为了对比！"); // 囧，这方法不错。如果要执行，请注释掉本行，并放开下面的代码。
        /*
        synchronized(taskQueue){ // FIXME 里面条件一直成立的话，会一直循环，这样就没法释放锁了吧？？？所以应该放到循环里面
            while(!taskQueue.isEmpty()){
                try{
                    taskQueue.wait(); // 只有添加的时候才会唤醒，这时没人唤醒了？
                } catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
        for(int i = 0; i < workerNum; i++){
            System.out.println("----stop");
            workerThreads[i].stopWorker();
        }
        */
    }


    /**
     * 内部类，工作线程
     */
    private class WorkerThread extends Thread {
        /*** 工作线程是否有效，用于结束该线程*/
        private boolean isRunning = true;

        public WorkerThread(String name){
            super(name);
        }

        @Override
        public void run(){
            Runnable r = null;
            while(isRunning){
                synchronized(taskQueue){// 只要还在执行，就随时准备取出新的任务
                    // 不管有没有结束，只要有任务，就取出来
                    if(!taskQueue.isEmpty()){
                        r = taskQueue.remove(0);
                        System.out.println("取到了一个任务");
                    }
                }
                // 如果取出的任务不为null，那就执行其run()
                if(r != null){
                    r.run(); // TODO 关键点
                    System.out.println("完成了一个任务");
                }
                executeTaskNumber++; // 执行过的任务数量加1
                r = null;
            }
        }

        /**
         * 通过设定flag来终止线程的执行
         */
        public void stopWorker(){
            isRunning = false;
        }
    }
}
